import Foundation

typealias DWObserverBlock = (Any) -> Void

class DWWeakProxy: NSProxy {
        
    fileprivate weak var target : AnyObject?
    
    class func proxyWithTarget (_ target : AnyObject) -> DWWeakProxy {
        let weakProxy = DWWeakProxy.alloc()
        weakProxy.target = target
        return weakProxy
    }
    
    override var hash: Int {
        return target?.hash ?? 0
    }
    
    override func responds(to aSelector: Selector!) -> Bool {
        return target?.responds(to: aSelector) ?? false
    }
    
    override func isEqual(_ object: Any?) -> Bool {
        return target?.isEqual(object) ?? false
    }
    
    override var superclass: AnyClass? {
        return target?.superclass ?? nil
    }
    
    override func isKind(of aClass: AnyClass) -> Bool {
        return target?.isKind(of: aClass) ?? false
    }
    
    override func isMember(of aClass: AnyClass) -> Bool {
        return target?.isMember(of: aClass) ?? false
    }
    
    override func conforms(to aProtocol: Protocol) -> Bool {
        return target?.conforms(to: aProtocol) ?? false
    }
    
    @discardableResult
    override func perform(_ aSelector: Selector!, with object: Any!) -> Unmanaged<AnyObject>? {
        return target?.perform(aSelector, with: object)
    }
    
    override func isProxy() -> Bool {
        return true
    }
    
    override var description: String {
        return target?.description ?? "DWWeakProxy description"
    }
    
    override var debugDescription: String {
        return target?.debugDescription ?? "DLWeakProxy debugDescription"
    }
}

fileprivate struct DWObserverModel {
    
    var observer : DWWeakProxy
    
    var aSelector : Selector?
    
    var block : DWObserverBlock?
    
}

class DWObserverCenter: NSObject {

    fileprivate lazy var dw_observerMap: [String:DWObserverModel] = [:]
    
    func dw_addObserver(_ observer : AnyObject, aSelector : Selector? = nil, block : DWObserverBlock? = nil) {
        guard let observerHash = observer.hash else { return }
        let key : String = dw_searchKey(observer) ?? ("\(observerHash)" + dw_randomKey())
        dw_observerMap[key] = DWObserverModel(observer: DWWeakProxy.proxyWithTarget(observer), aSelector: aSelector, block: block)
    }
    
    fileprivate func dw_searchKey(_ observer : AnyObject) -> String? {
        for element in dw_observerMap.enumerated() {
            if element.element.value.observer.isEqual(observer) {
                return element.element.key
            }
        }
        return nil
    }
    
    fileprivate func dw_randomKey() -> String {
        let letters = "abcdefghijklmnopqrstuvwxyz"
        var randomString = ""
        for _ in 0..<5 {
            let randomIndex = Int(arc4random_uniform(UInt32(letters.count)))
            let randomCharacter = letters[letters.index(letters.startIndex, offsetBy: randomIndex)]
            randomString.append(randomCharacter)
        }
        return randomString
    }

    
    func dw_sendObserverMsg(_ msg : Any) {
        for element in dw_copyObserverMap() {
            guard let _ = element.value.observer.target else {
                dw_observerMap.removeValue(forKey: element.key)
                return
            }
            if let aSelector = element.value.aSelector, element.value.observer.responds(to: aSelector) {
                element.value.observer.perform(aSelector, with: msg)
            }
            if let block = element.value.block {
                block(msg)
            }
        }
    }
    
    private func dw_copyObserverMap() -> [String : DWObserverModel] {
        var map : [String : DWObserverModel] = [:]
        for element in self.dw_observerMap.enumerated() {
            map[element.element.key] = element.element.value
        }
        return map
    }
        
}
